package com.tpay.server.util;

import java.util.ArrayList;
import java.util.List;

import javax.persistence.EntityManager;

import org.dbunit.database.DatabaseConfig;
import org.dbunit.database.DatabaseConnection;
import org.dbunit.database.IDatabaseConnection;
import org.dbunit.dataset.IDataSet;
import org.dbunit.dataset.xml.FlatXmlDataSetBuilder;
import org.dbunit.ext.oracle.Oracle10DataTypeFactory;
import org.dbunit.operation.DatabaseOperation;
import org.hibernate.impl.SessionImpl;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.runner.RunWith;

import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.persist.UnitOfWork;
import com.tpay.server.GeneralDao;
import com.tpay.server.util.GuiceUnitilsJUnit4ClassRunner.MyGuiceModules;

@RunWith(GuiceUnitilsJUnit4ClassRunner.class)
@MyGuiceModules({MyGuiceModule.class})
public class GuiceUnitilsJUnit4 {
	public static int CNT_APP = 3;
	public static int CNT_CGROUP = 3;
	public static int CNT_CGROUPAPP = 4;
	public static int CNT_GROUP = 3;
	
	@Inject protected EntityManager em;
	@Inject protected UnitOfWork work;
	@Inject protected GeneralDao dao;
	
	private static boolean inited = false;
	
	protected static IDatabaseConnection connection;
	protected static FlatXmlDataSetBuilder flatBuilder;
	
	static {
		initialize(new MyGuiceModule());
	}
	
	private static void initialize(AbstractModule module) {
		try {
			// @Inject의 EntityManager는 GuiceUnitilsJUnit4 클래스가 생성된 후에 생성되므로
			// 또한 @BeforeClass 에서는 Injector의 인스턴스를 생성할 수 없다.
			Injector injector = Guice.createInjector(module);
			EntityManager entityManager = injector.getInstance(EntityManager.class);
	
			connection = new DatabaseConnection(((SessionImpl)(entityManager.getDelegate())).connection(), "TERPDEV");
			connection.getConfig().setProperty(DatabaseConfig.PROPERTY_DATATYPE_FACTORY, new Oracle10DataTypeFactory());
			connection.getConnection().setAutoCommit(true);	
			
			flatBuilder = new FlatXmlDataSetBuilder();
			flatBuilder.setColumnSensing(true);
		} catch(Exception e) {
			e.printStackTrace();
		}
	}
	
	private static Class<?> classType;
	
	public GuiceUnitilsJUnit4() {
		classType = this.getClass();
	}
	
	private static List<String> insertedFirstFiles;
	
	public GuiceUnitilsJUnit4(String... dataSetFileNames) {
		this();
		
		// @BeforeClass 역할
		try {
			if(inited == false) {
				inited = true;
			
				insertedFirstFiles = new ArrayList<String>();
				
				for(String dataSetFileName : dataSetFileNames) {
					IDataSet dataset = flatBuilder.build(classType.getResourceAsStream(dataSetFileName));
					DatabaseOperation.CLEAN_INSERT.execute(connection, dataset);
					
					insertedFirstFiles.add(dataSetFileName);
				}
			}
		} catch(Exception e) {
			e.printStackTrace();
		}
	}

	@After
	public void after() throws Exception {
		// 테스트 메소드를 연이어서 실행할 때 영속성 컨텍스트를 비우기 위해서
		// 이렇게 깨끗이 비우면 다음 테스트 메소드 실행시 DB로 쿼리가 날라간다.
		em.clear();	
		
		// unitils로 입력된 순서의 역순으로 삭제
		if(insertedFiles == null || insertedFiles.size() == 0)
			return;
		
		for(int i = insertedFiles.size()-1; i >= 0; i--) {
			String fileName = insertedFiles.get(i);

			IDataSet dataset = flatBuilder.build(classType.getResourceAsStream(fileName));
			DatabaseOperation.DELETE_ALL.execute(connection, dataset);
		}
	}
	
	@AfterClass
	public static void afterClass() throws Exception {
		if(insertedFirstFiles == null || insertedFirstFiles.size() == 0)
			return;
		
		for(int i = insertedFirstFiles.size()-1; i >= 0; i--) {
			String fileName = insertedFirstFiles.get(i);

			IDataSet dataset = flatBuilder.build(classType.getResourceAsStream(fileName));
			DatabaseOperation.DELETE_ALL.execute(connection, dataset);
		}
	}
	
	private List<String> insertedFiles;
	
	private void insert(String dataSetFileName) throws Exception {
		if(insertedFiles == null)
			insertedFiles = new ArrayList<String>();
		
		IDataSet dataset = flatBuilder.build(this.getClass().getResourceAsStream(dataSetFileName));
		DatabaseOperation.INSERT.execute(connection, dataset);
		
		insertedFiles.add(dataSetFileName);
	}
	
	protected void insertApp() throws Exception {
		insert("data/App.xml");
	}
	
	protected void insertMenu() throws Exception {
		insert("data/Menu.xml");
	}
	
	protected void insertCgroup() throws Exception {
		insert("data/Cgroup.xml");
	}
	
	protected void insertCgroupApp() throws Exception {
		insert("data/CgroupApp.xml");
	}
	
	protected void insertCgroupMenu() throws Exception {
		insert("data/CgroupMenu.xml");
	}
	
	protected void insertGroup() throws Exception {
		insert("data/Group.xml");
	}
	
	protected void insertUser() throws Exception {
		insert("data/User.xml");
	}
}
